#include <unistd.h>      // 提供 POSIX API（如 getpid、fork、close 等）
#include <stdlib.h>      // 提供标准库函数（如 exit）
#include <fcntl.h>       // 提供文件控制选项（如 open、flock）
#include <syslog.h>      // 提供系统日志功能（如 syslog）
#include <string.h>      // 提供字符串操作函数（如 strerror、strlen）
#include <errno.h>       // 提供错误码（如 errno）
#include <stdio.h>       // 提供标准输入输出函数（如 sprintf）
#include <sys/stat.h>    // 提供文件状态信息（如文件权限）
#include <sys/file.h>    // 提供文件锁定功能（如 flock）

#define LOCKFILE "/var/run/daemon.pid"  // PID 文件路径
#define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)  // 文件权限：用户读写，组和其他用户只读，0644

int already_running(void);

int main(void)
{
    int flag;
    // 打开系统日志连接（标识程序名，选项，设施）
    openlog("single_instance_daemon_test", LOG_PID, LOG_DAEMON);

    // 检查是否已有实例运行
    flag = already_running();
    printf("flag = %d\n", flag);
    if (flag) {
        syslog(LOG_ERR, "Program is already running. Exiting.");
        fprintf(stderr, "Error: Program is already running.\n");
        closelog();
        exit(1);
    }

    // 当前是唯一实例，打印提示信息
    syslog(LOG_INFO, "Starting single instance daemon.");
    printf("Daemon started successfully. PID: %ld\n", (long)getpid());

    // 模拟守护进程工作（保持进程运行）
    while (1) {
        sleep(5); // 每5秒打印一次心跳信息
        syslog(LOG_INFO, "Daemon is running...");
        printf("Daemon heartbeat...\n");
    }

    // 清理资源（通常守护进程不会执行到这里）
    closelog();
    return 0;
}


// 检查是否已有实例运行，没有则返回 0
int already_running(void)
{   
    int     fd;          // 文件描述符
    char    buf[16];     // 存储 PID 的缓冲区

    // 打开 PID 文件，若不存在则创建
    fd = open(LOCKFILE, O_RDWR|O_CREAT, LOCKMODE);
    if (fd < 0) {
        // 打开文件失败，记录日志并退出
        syslog(LOG_ERR, "can't open %s: %s", LOCKFILE, strerror(errno));
        exit(1);
    }

    // 尝试对文件加非阻塞模式排他锁（写锁）
    if (flock(fd, LOCK_EX | LOCK_NB) < 0) { // 阻塞模式会导致二次运行程序卡在这里
        if (errno == EACCES || errno == EAGAIN) {
            // 文件已被锁定，说明已有实例运行
            close(fd);
            return 1;  // 返回 1 表示已有实例运行
        }
        // 加锁失败，记录日志并退出
        syslog(LOG_ERR, "can't lock %s: %s", LOCKFILE, strerror(errno));
        exit(1);
    }

    // 清空文件内容
    ftruncate(fd, 0);

    // 将当前进程的 PID 写入文件
    sprintf(buf, "%ld", (long)getpid());
    write(fd, buf, strlen(buf) + 1);

    // 返回 0 表示当前是唯一实例
    return 0;
}


